﻿using FFmpeg.AutoGen;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace VideoPlayer.FFmpegHandle
{
    public unsafe class AudioStreamDecoder : IDisposable
    {

        private readonly AVCodecContext* _pCodecContext;
        private readonly AVFormatContext* _pFormatContext;
        private readonly int _streamIndex;
        private readonly AVFrame* _pFrame;
        private readonly AVPacket* _pPacket;
        private readonly SwrContext* _pswrCtx;
        private byte** _out_buffer;
        public AudioStreamDecoder(string url)
        {
            _pFormatContext = ffmpeg.avformat_alloc_context();

            var pFormatContext = _pFormatContext;
            ffmpeg.avformat_open_input(&pFormatContext, url, null, null).ThrowExceptionIfError();

            ffmpeg.avformat_find_stream_info(_pFormatContext, null).ThrowExceptionIfError();

            // find the first video stream
            AVStream* pStream = null;
            for (var i = 0; i < _pFormatContext->nb_streams; i++)
                if (_pFormatContext->streams[i]->codec->codec_type == AVMediaType.AVMEDIA_TYPE_AUDIO)
                {
                    pStream = _pFormatContext->streams[i];
                    break;
                }

            if (pStream == null) throw new InvalidOperationException("Could not found video stream.");

            _streamIndex = pStream->index;
            _pCodecContext = pStream->codec;

            var codecId = _pCodecContext->codec_id;

            var pCodec = ffmpeg.avcodec_find_decoder(codecId);
            if (pCodec == null) throw new InvalidOperationException("Unsupported codec.");

            ffmpeg.avcodec_open2(_pCodecContext, pCodec, null).ThrowExceptionIfError();

            CodecName = ffmpeg.avcodec_get_name(codecId);
            FrameSize = new Size(_pCodecContext->width, _pCodecContext->height);
            PixelFormat = _pCodecContext->pix_fmt;

            _pPacket = ffmpeg.av_packet_alloc();
            _pFrame = ffmpeg.av_frame_alloc();


            _pswrCtx = ffmpeg.swr_alloc();
            //重采样设置选项-----------------------------------------------------------start
            //输入的采样格式
            var in_sample_fmt = _pCodecContext->sample_fmt;
            //输出的采样格式 16bit PCM
            var out_sample_fmt = AVSampleFormat.AV_SAMPLE_FMT_S16;
            //输入的采样率
            int in_sample_rate = _pCodecContext->sample_rate;
            //输出的采样率
            int out_sample_rate = 44100;
            //输入的声道布局
            ulong in_ch_layout = _pCodecContext->channel_layout;
            //输出的声道布局
            int out_ch_layout = ffmpeg.AV_CH_LAYOUT_MONO;

            ffmpeg.swr_alloc_set_opts(_pswrCtx, out_ch_layout, out_sample_fmt, out_sample_rate, (long)in_ch_layout, in_sample_fmt,
                       in_sample_rate, 0, null);
            int t = ffmpeg.swr_init(_pswrCtx);
            //重采样设置选项-----------------------------------------------------------end
            //获取输出的声道个数
            int out_channel_nb = ffmpeg.av_get_channel_layout_nb_channels((ulong)out_ch_layout);
            //存储pcm数据
            _out_buffer = (byte**)ffmpeg.av_malloc((ulong)2 * 44100);
            //FILE* fp_pcm = fopen("out.pcm", "wb");
            //int ret, got_frame, framecount = 0;


            //ffmpeg.avcodec_register_all();
            //var avcodec = ffmpeg.avcodec_find_decoder_by_name(vsd.CodecName);
            //ffmpeg.avcodec_alloc_context3(avcodec);
        }

        public string CodecName { get; }
        public Size FrameSize { get; }
        public AVPixelFormat PixelFormat { get; }

        public void Dispose()
        {
            ffmpeg.av_frame_unref(_pFrame);
            ffmpeg.av_free(_pFrame);

            ffmpeg.av_packet_unref(_pPacket);
            ffmpeg.av_free(_pPacket);

            ffmpeg.avcodec_close(_pCodecContext);
            var pFormatContext = _pFormatContext;
            ffmpeg.avformat_close_input(&pFormatContext);
        }

        public bool TryDecodeNextFrame(out byte frame)
        {
            ffmpeg.av_frame_unref(_pFrame);
            int error;
            do
            {
                try
                {
                    do
                    {
                        error = ffmpeg.av_read_frame(_pFormatContext, _pPacket);
                        if (error == ffmpeg.AVERROR_EOF)
                        {
                            //frame = *_pFrame;
                            frame = **_out_buffer;
                            return false;
                        }

                        error.ThrowExceptionIfError();
                    } while (_pPacket->stream_index != _streamIndex);

                    ffmpeg.avcodec_send_packet(_pCodecContext, _pPacket).ThrowExceptionIfError();
                }
                finally
                {
                    ffmpeg.av_packet_unref(_pPacket);
                }

                error = ffmpeg.avcodec_receive_frame(_pCodecContext, _pFrame);
            } while (error == ffmpeg.AVERROR(ffmpeg.EAGAIN));

            ffmpeg.swr_convert(_pswrCtx, _out_buffer, 2 * 44100, _pFrame->extended_data, _pFrame->nb_samples);
            frame = **_out_buffer;
            error.ThrowExceptionIfError();
            //frame = *_pFrame;
            return true;
        }

    }
}
